1 /**
2 *   Public imports free, malloc, realloc, memcpy, memcmp, memset.
3 *   - toHeap!T executes malloc + memcpy
4 *   - alloc!T is a malloc with the type size
5 *   - allocSlice!T will return a heap allocated slice 
6 *
7 */
8 module hip.util.memory;
9 
10 public import core.stdc.stdlib;
11 public import core.stdc.string:memcpy, memcmp, memset;
12 import hip.util.reflection;
13 
14 version(WebAssembly) version = CustomRuntime;
15 version(PSVita) version = CustomRuntime;
16 version(CustomRuntimeTest) version = CustomRuntime;
17 @nogc:
18 void setZeroMemory(T)(ref T variable)
19 {
20     memset(&variable, 0, T.sizeof);
21 }
22 
23 
24 T* alloc(T)(size_t count = 1){return cast(T*)core.stdc.stdlib.malloc(T.sizeof*count);}
25 T[] allocSlice(T)(size_t count){return alloc!T(count)[0..count];}
26 
27 void* toHeap(T)(in T data) if(isReference!T)
28 {
29     version(CustomRuntime)
30     {
31         void* m = cast(void*)data; //WASM don't need to allocate as it is not ever deleted.
32     }
33     else
34     {
35         import core.memory;
36         void* m = cast(void*)data;
37         GC.addRoot(cast(void*)data);
38     }
39     return m;
40 }
41 
42 void* toHeap(T)(T data) if(!isReference!T)
43 {
44     void* m = alloc!T;
45     memcpy(m, &data, T.sizeof);
46     return m;
47 }
48 
49 void[] toHeapSlice(T)(T data) if(!is(T == void[]))
50 {
51     return toHeap(data)[0..T.sizeof];
52 }
53 
54 
55 void freeGCMemory(void* data, string f = __FILE__, size_t l = __LINE__)
56 {
57     assert(data !is null, "Tried to free null data.");
58     version(CustomRuntime)
59     {
60         static import rt.hooks;
61         rt.hooks.free(cast(ubyte*)data, f, l);
62     }
63     else
64     {
65         import core.memory;
66         GC.removeRoot(data);
67         GC.free(data);
68     }
69 }
70 
71 void freeGCMemory(ref void* data, string f = __FILE__, size_t l = __LINE__) //Remove ref.
72 {
73     assert(data !is null, "Tried to free null data.");
74     version(CustomRuntime)
75     {
76         static import rt.hooks;
77         rt.hooks.free(cast(ubyte*)data, f, l);
78     }
79     else
80     {
81         import core.memory;
82         GC.removeRoot(data);
83         GC.free(data);
84     }
85     data = null;
86 }
87 
88 void freeGCMemory(ref void[] data, string f = __FILE__, size_t l = __LINE__)
89 {
90     assert(data.length, "Tried to free null data.");
91     freeGCMemory(data.ptr, f, l);
92     data = null;
93 }
94 
95 void freeGCMemory(T)(ref T[] data, string f = __FILE__, size_t l = __LINE__)
96 {
97     freeGCMemory(cast(void*)data.ptr, f, l);
98     data = null;
99 }
100 
101 
102 class Pool(T) if(is(T == class) || is(T == interface))
103 {
104     private
105     {
106         T[] objects;
107         int deadCount, maxPoolSize = -1;
108         pragma(inline, true) T getFirstDead(){return objects[getActiveCount];}
109 
110         struct TInterface
111         {
112             pragma(inline, true)
113             {
114                 static void deinitialize(T obj){obj.deinitialize();}
115                 static void initialize(T obj){obj.initialize();}
116             }
117         }
118     }
119 
120     /** 
121      * 
122      * Params:
123      *   maxPoolSize = -1 means that Pool will never return null. It may still give array out of bounds if too many objects are created.
124      */
125     this(int maxPoolSize = -1)
126     {
127         this.maxPoolSize = maxPoolSize;
128     }
129     int getActiveCount() => cast(int)objects.length - deadCount;
130     
131     T get(Args...)(Args a)
132     {
133         T ret;
134         if(deadCount > 0)
135         {
136             ret = getFirstDead();
137             deadCount--;
138         }
139         else
140         {
141             if(objects.length + 1 > maxPoolSize) return null;
142             int activeCount = getActiveCount();
143             objects.length++;
144             if(deadCount)
145                 objects[$-1] = objects[activeCount];
146             objects[activeCount] = ret = new T(a);
147         }
148         TInterface.initialize(ret);
149         return ret;
150     }
151 
152     int opApply(scope int delegate(ref T) dg)
153     {
154         int result = 0;
155         
156         foreach (i; 0..getActiveCount)
157         {
158             result = dg(objects[i]);
159             if (result)
160                 break;
161         }
162         return result;
163     }
164 
165     void kill(T instance)
166     {
167         TInterface.deinitialize(instance);
168         deadCount++;
169     }
170     void clear()
171     {
172         foreach(obj; this) TInterface.deinitialize(obj);
173         deadCount = cast(int)objects.length;
174     }
175 }